package com.ra.landlord;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Where;

import com.googlecode.cswish.annotation.QLFormula;
import com.googlecode.cswish.annotation.cm;
import com.ra.landlord.constant.RoomType;
import com.ra.system.Version;
import com.ra.util.Address;
import com.ra.util.DateTimeUtil;

/**
 * 房间信息表
 * 
 * @author Jerry.Feng Date: 2014-07-31
 */
@Entity
@Table(name = "ra_room")
@Where(clause = "data_status = 1")
@FilterDefs({
	@FilterDef(name = "tenantUserNameFilter", parameters = @ParamDef(name = "tenantUserName", type="string")), 
	@FilterDef(name = "rentStateFilter", parameters = @ParamDef(name = "rentState", type="int")),
	@FilterDef(name = "rentEndDateFilter", parameters = @ParamDef(name = "rentEndDate", type="string"))
})
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Room implements Version, LandlordOwner {
	
	@cm("已出租")
	public final static Integer RENT_STATE_ALREADY = 0;
	@cm("未出租")
	public final static Integer RENT_STATE_NOYET = 1;
	@cm("预定")
	public final static Integer RENT_STATE_BOOK = 2;
	@cm("转租")
	public final static Integer RENT_STATE_CHANGE = 3;
	@cm("隐藏")
	public final static Integer RENT_STATE_HIDDEN = 4;
	
	@cm("整租")
	public final static Integer RENT_TYPE_ENTIRE = 0;
	@cm("分租")
	public final static Integer RENT_TYPE_SUB = 1;

	private Integer id;
	
	private String no;
	
	private String name;
	
	// 此处房租和合约房租不一样，合约房租签订合约房租后就不会变，room里的房租可能递增 (由于兼容PC版本类型定义为了String)
	private String rental;
	
	//押金
	private BigDecimal deposit;
	
	//描述几房几厅
	private Integer roomType; 
	
	// 冗余: Building.address
	private Address address;
	
	// TODO: 面积, 类型用BigDecimal?
	private BigDecimal area;
	
	// 房屋朝向
	private Integer orientation;
	
	// 照片序号，目前照片以数字排序存于固定的目录下
	private Integer pictureNo;
	
	// 租借状态(0:已出租；1：未出租；2：预定；3：转租；4：隐藏)
	private Integer rentState;
	
	// 房屋用途: 0：出租		1：商用
	private Integer purpose;
	
	// 冗余: BuildingFloor.building
	private Building building;
	
	private BuildingFloor buildingFloor;
	
	// 标配的标志， 用于搜房： 比方有无宽带，空调，洗手间等
	// room_config
	private Integer config;
	
	// 收费规则
	private List<FeeRule> feeRules;
	
	// transient, 指定某一计费规则下， 该规则的本次读表数据和上次读表数据
	private Meter currentMeter;

	// transient
	private Meter lastMeter;
	
	// transient
	private Meter lastLastMeter;
	
	// 实际的采购配置
	private List<RoomItem> roomItems;
	
	// 分摊单元
	private List<RoomShareUnit> roomShareUnits;
	
	// 所有费用（包含已收、未收）
	private List<Fee> fees;
	
	// 冗余fees, 仅取未付的房费
	//(unpayFees这个状态目前暂时没用，data_status=1和data_status=2都为已收租，这里假设以后要用，所以不去掉代码，对应的数据data_status=3)
	private List<Fee> unpayFees;
	
	//transient, 押金对应的费用，房屋出租时产生
	private Fee depositFee;

	// transient, 本次应收费用, 从实时性上, 应按起始与结束日期判断 (仅app使用)
	private Fee receivableFee;
	
	// transient, 上次实收费用, 从实时性上, 应按起始与结束日期判断(仅app使用)
	private Fee lastFee;
	
	// transient, 已经出租标志
	private Boolean hasRent;
	
	// transient, 已经交租
	private Boolean hasPay;
	
	// 有效合约
	private ValidAgreement agreement;
	
	// 全部合约
	private List<Agreement> agreements;
	
	// 房间附件: 照片, 视频等
	private List<RoomAttachment> roomAttachments;
	
	//房间配置常量描述，用于前端展示
	//private String roomConfigDesc;
	
	private LandlordOwnerUser landlordUser;
	
	//备注
	private String remark;
	
	// transient,最早入住日期
	private Date earliestDate;
	
	// transient, 是否能撤销退租
	private Boolean isUndoThrowALease;
	
	// 创建时间
	private Date createTime;
	
	private Integer version;
	
	private Integer status;
	
	// transient,租客名称
	private String tenantUserName;
	
	// transient,是否存在未缴费的订单
	private Boolean hasTodoOrder;
	
	private Integer rentalOrderId;
	
	@cm("出租类型")
	private Integer rentType;
	
	@cm("几室")
	private Integer roomCount;
	
	@cm("几厅")
	private Integer hallCount;
	
	@cm("几卫")
	private Integer toiletCount;
	
	@cm("押几 ") 
	private Integer depositNum;
	
	@cm("付几") 
	private Integer payNum;
	
	@cm("房屋设置，取代以前的config")
	private RoomConfig roomConfig;
	
	@cm("子房间")
	private List<SubRoom> subRooms;

	//Trasient 是否需要添加押金收费项
	private Boolean needAddDeposit;
	
	@Transient
	public String getTenantUserName() {
		return tenantUserName;
	}

	public void setTenantUserName(String tenantUserName) {
		this.tenantUserName = tenantUserName;
	}

	//@EmbeddedId
	@Id
	@GeneratedValue
	@Column(name = "room_id")
	@Index(name="idx_room_name")
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name = "room_num")
	public String getNo() {
		return no;
	}

	public void setNo(String no) {
		this.no = no;
	}

	@Column(name = "room_name")
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Column(name = "rental")
	public String getRental() {
		if (StringUtils.isNotBlank(rental)) {
			return BigDecimal.valueOf(Double.valueOf(rental)).setScale(2, RoundingMode.HALF_UP).toString();
		}
		return null;
	}

	public void setRental(String rental) {
		this.rental = rental;
	}
	
	@ManyToOne(targetEntity=Address.class, fetch=FetchType.LAZY, optional=true, cascade=CascadeType.MERGE)
	@JoinColumn(name = "area_id")
	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Column(name = "room_state")
	public Integer getRentState() {
		return rentState;
	}

	public void setRentState(Integer rentState) {
		this.rentState = rentState;
	}

	@Column(name = "room_funtion")
	public Integer getPurpose() {
		return purpose;
	}

	public void setPurpose(Integer purpose) {
		this.purpose = purpose;
	}

	@ManyToOne(targetEntity=Building.class, fetch=FetchType.LAZY, optional=false)
	@JoinColumn(name = "buildings_id")
	@Index(name="idx_ra_room_buildings_id")
	public Building getBuilding() {
		return building;
	}

	public void setBuilding(Building building) {
		this.building = building;
	}

	@Column(name = "room_area")
	public BigDecimal getArea() {
		return area;
	}

	public void setArea(BigDecimal area) {
		this.area = area;
	}
	
	@Column(name = "room_face")
	public Integer getOrientation() {
		return orientation;
	}

	public void setOrientation(Integer orientation) {
		this.orientation = orientation;
	}
	
	@Column(name = "pictures_id")
	public Integer getPictureNo() {
		return pictureNo;
	}

	public void setPictureNo(Integer pictureNo) {
		this.pictureNo = pictureNo;
	}

	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name = "floor_id")
	@Index(name="idx_buildingFloor_id")
	public BuildingFloor getBuildingFloor() {
		return buildingFloor;
	}

	public void setBuildingFloor(BuildingFloor buildingFloor) {
		this.buildingFloor = buildingFloor;
	}

	@Column(name = "room_config")
	public Integer getConfig() {
		return config;
	}

	public void setConfig(Integer config) {
		this.config = config;
	}
	
	
	@OneToMany(targetEntity=RoomItem.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="room")
	public List<RoomItem> getRoomItems() {
		return roomItems;
	}

	public void setRoomItems(List<RoomItem> roomItems) {
		this.roomItems = roomItems;
	}
	
	@OneToMany(targetEntity=RoomShareUnit.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="room")
	public List<RoomShareUnit> getRoomShareUnits() {
		return roomShareUnits;
	}

	public void setRoomShareUnits(List<RoomShareUnit> roomShareUnits) {
		this.roomShareUnits = roomShareUnits;
	}

	@OneToMany(targetEntity = Fee.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="room")
	@Where(clause = "data_status=1 or data_status=2")
	public List<Fee> getFees() {
		return fees;
	}

	public void setFees(List<Fee> fees) {
		this.fees = fees;
	}
	
	//unpayFees这个状态目前没有用了
	@OneToMany(targetEntity = Fee.class, fetch=FetchType.LAZY, mappedBy="room")
	@Where(clause = "data_status=3")
	@BatchSize(size = 2)
	public List<Fee> getUnpayFees() {
		return unpayFees;
	}

	public void setUnpayFees(List<Fee> unpayFees) {
		this.unpayFees = unpayFees;
	}

	//@ManyToOne(cascade=CascadeType.ALL)
	@Transient
	public Fee getReceivableFee() {
		return receivableFee;
	}

	public void setReceivableFee(Fee receivableFee) {
		this.receivableFee = receivableFee;
	}
	
	@Transient
	public Fee getDepositFee() {
		return depositFee;
	}

	public void setDepositFee(Fee depositFee) {
		this.depositFee = depositFee;
	}
	
	@OneToOne(targetEntity=ValidAgreement.class, mappedBy="room", cascade = {CascadeType.ALL}, orphanRemoval=true,  fetch=FetchType.LAZY, optional=true)
	@LazyToOne(LazyToOneOption.PROXY)
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	public ValidAgreement getAgreement() {
		return this.agreement;
	}

	public void setAgreement(ValidAgreement agreement) {
		this.agreement = agreement;
	}
	
	
	@OneToMany(targetEntity=Agreement.class, mappedBy="room", fetch=FetchType.LAZY,
			cascade = {CascadeType.ALL}, orphanRemoval=true)
	@OrderBy("id desc")
	public List<Agreement> getAgreements() {
		return agreements;
	}

	public void setAgreements(List<Agreement> agreements) {
		this.agreements = agreements;
	}

	@OneToMany(targetEntity=FeeRule.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="room")
	@Where(clause = "data_status=1")
	public List<FeeRule> getFeeRules() {
		return feeRules;
	}

	public void setFeeRules(List<FeeRule> feeRules) {
		this.feeRules = feeRules;
	}
	
	@Transient
	public Meter getCurrentMeter() {
		return currentMeter;
	}

	public void setCurrentMeter(Meter currentMeter) {
		this.currentMeter = currentMeter;
	}

	@Transient
	public Meter getLastMeter() {
		return lastMeter;
	}

	public void setLastMeter(Meter lastMeter) {
		this.lastMeter = lastMeter;
 	}

	@Transient
	@QLFormula(value=":qlFormulaForHasRent", isColumn=false)
	public Boolean getHasRent() {
		return hasRent;
	}
	
	public static String qlFormulaForHasRent(Map<String, Object> model) {
		Object hasRent = model.get("hasRent");
		if (hasRent == null) {
			return "";
		} else if ("true".equals(hasRent) || Boolean.TRUE.equals(hasRent)) {
			// 房子存在于合约的房子中 and a.agreement.agreementState = 1
			//return "(a.rentState = 0 or a.rentState = 2 or a.rentState = 3)";
			return "(a.rentState = 0 or a.rentState = 2 or a.rentState = 3)";
		} else {
			// 不存在(房子在合约中出现） and b.agreementState = 1
			//return "not exists(select a.id from com.rentapartment.landlord.Agreement b where a.id=b.room.id and b.agreementState = 1)";
			return "(a.rentState = 1 or a.rentState = 4)";
			// "a.id not in (select b.room.id from com.rentapartment.landlord.Agreement b)";
		}
	}

	public void setHasRent(Boolean hasRent) {
		this.hasRent = hasRent;
	}
	
	public boolean checkIsRent() {
		return rentState == 0 || rentState == 2 || rentState == 3;
	}

	@Transient
	public Boolean getHasPay() {
		return hasPay;
	}

	public void setHasPay(Boolean hasPay) {
		this.hasPay = hasPay;
	}
	
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name = "createuserid")
	@Index(name="idx_ra_room_createuserid")
	public LandlordOwnerUser getLandlordUser() {
		return landlordUser;
	}

	public void setLandlordUser(LandlordOwnerUser landlordUser) {
		this.landlordUser = landlordUser;
	}
	
	@Column(name = "version_id")
	public Integer getVersion() {
		return version;
	}

	public void setVersion(Integer version) {
		this.version = version;
	}
	
	@Column(name = "data_status")
	@Index(name="idx_ra_room_creator_status",columnNames={"createuserid","data_status","buildings_id"})
	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}
	
	//@ManyToOne(cascade=CascadeType.ALL)
	@Transient
	public Fee getLastFee() {
		return lastFee;
	}

	public void setLastFee(Fee lastFee) {
		this.lastFee = lastFee;
	}
	
	@Column(name = "room_type")
	public Integer getRoomType() {
		return roomType;
	}

	public void setRoomType(Integer roomType) {
		this.roomType = roomType;
	}
	
	/**
	 * 自动适配新老字段
	 */
	@Transient
	public void judgeDoAdapt()
	{
		if (this.roomCount != null)
		{
			adaptRoomTypeNew();
		}
		else if (this.roomType != null)
		{
			adaptRoomType();
		}
	}
	
	/**
	 * 兼容老字段: 适配新字段到roomType(新适配老)
	 */
	@Transient
	public void adaptRoomTypeNew()
	{
		if (this.roomCount == null)
		{
			return;
		}
		
		switch (this.roomCount) 
		{
		case 1:
			if (this.hallCount == 0)
			{
				setRoomType(RoomType.RTM_1R_0H);
			}
			else 
			{
				setRoomType(RoomType.RTM_1R_1H);
			}
			break;
		case 2:
			if (this.hallCount == 1)
			{
				setRoomType(RoomType.RTM_2R_1H);
			}
			else 
			{
				setRoomType(RoomType.RTM_2R_2H);
			}
			break;
		case 3:
			if (this.hallCount == 1)
			{
				setRoomType(RoomType.RTM_3R_1H);
			}
			else 
			{
				setRoomType(RoomType.RTM_3R_2H);
			}
			break;
	
		default:
			setRoomType(RoomType.RTM_3R_2H);
		}
	}
	
	/**
	 * 兼容新字段: 适配roomType到多个字段值(老适配新)
	 */
	@Transient
	public void adaptRoomType()
	{
		if (this.roomType == null || this.roomType == 0)
		{
			return;
		}
		switch (this.roomType) 
		{
		case 1:
			adaptRoomTypeSet(1, 0, 0);
			break;
		case 2:
			adaptRoomTypeSet(1, 1, 1);
			break;
		case 4:
			adaptRoomTypeSet(2, 1, 1);
			break;
		case 8:
			adaptRoomTypeSet(2, 2, 1);
			break;
		case 16:
			adaptRoomTypeSet(3, 1, 1);
			break;
		case 32:
			adaptRoomTypeSet(3, 2, 1);
			break;
		case 64:
			adaptRoomTypeSet(3, 3, 1);
			break;
		case 128:
			adaptRoomTypeSet(4, 1, 1);
			break;
		case 256:
			adaptRoomTypeSet(4, 2, 1);
			break;
		case 512:
			adaptRoomTypeSet(4, 3, 1);
			break;
		case 1024:
			adaptRoomTypeSet(4, 4, 1);
			break;
		default:
			adaptRoomTypeSet(2, 1, 1);
		}
	}
	
	@Transient
	private void adaptRoomTypeSet(int room, int hall, int toilet)
	{
		this.roomCount = room;
		this.hallCount = hall;
		this.toiletCount = toilet;
	}
	
	@Transient
	public String getRoomTypeDesc() {
		return RoomType.ROOM_TYPE_DESC.get(getRoomType());
	}
	
	@Transient
	public Agreement getLastAgreement() {
		
		Agreement lastAgreement = null;
		List<Agreement> lstagreement = getAgreements();
		if (lstagreement != null && lstagreement.size() > 0) {
			lastAgreement = lstagreement.get(0);
		}
		
		return lastAgreement;
	}
	
	@OneToMany(mappedBy="room", cascade=CascadeType.ALL, orphanRemoval=true,
			fetch=FetchType.LAZY, targetEntity=RoomAttachment.class)
	public List<RoomAttachment> getRoomAttachments() {
		return roomAttachments;
	}

	public void setRoomAttachments(List<RoomAttachment> roomAttachments) {
		this.roomAttachments = roomAttachments;
	}
	
	//获取房间配置二进制表示法
	//显示是这么个情况(二进制，一个位表示一个配置，1表示有，0表示无)
	//位置对应顺序为（11111111111   ->  热水器|冰箱|床|电视|洗衣机|宽带|桌|空调|洗手间|厨房|阳台|）
	@Transient
	public String getRoomConfigDesc() {
		String strDesc = getConfig() == null ? "" : Integer.toBinaryString(getConfig());
		String complementChar = "";
		for (int i = 0; i < 11 - strDesc.length(); i++) {
			complementChar += "0";
		}
		return complementChar + strDesc;
	}

	// 设置房间配置，常用的房间配置（二进制转十进制）
	public void setRoomConfigDesc(String roomConfigDesc) {
		if(StringUtils.isBlank(roomConfigDesc)){
			roomConfigDesc = "0";
		}
		setConfig(Integer.valueOf(roomConfigDesc,2));
	}
	
	@Column(name = "strRemark")
	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}
	
	@Transient
	public Date getEarliestDate() {
		ValidAgreement agreement = getAgreement();
		if(agreement != null) {
			String rentEndDate = agreement.getRentEndDate();
			return DateTimeUtil.StrToDate(rentEndDate);
		} else {
			return null;
		}
	}

	public void setEarliestDate(Timestamp earliestDate) {
		this.earliestDate = earliestDate;
	}

	@Transient
	public Boolean getIsUndoThrowALease() {
		return isUndoThrowALease;
	}
	
	public void setIsUndoThrowALease(Boolean isUndoThrowALease) {
		this.isUndoThrowALease = isUndoThrowALease;
	}
	
	@Temporal(TemporalType.TIMESTAMP)
	public Date getCreateTime() {
		return createTime;
	}

	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}

	public BigDecimal getDeposit()
	{
		return deposit;
	}

	public void setDeposit(BigDecimal deposit)
	{
		this.deposit = deposit;
	}

	@Transient
	public Meter getLastLastMeter() {
		return lastLastMeter;
	}

	public void setLastLastMeter(Meter lastLastMeter) {
		this.lastLastMeter = lastLastMeter;
	}
	
	@Transient
	public Boolean getHasTodoOrder() {
		return hasTodoOrder;
	}

	public void setHasTodoOrder(Boolean hasTodoOrder) {
		this.hasTodoOrder = hasTodoOrder;
	}
	
	@Transient
	public Integer getRentalOrderId() {
		return rentalOrderId;
	}

	public void setRentalOrderId(Integer rentalOrderId) {
		this.rentalOrderId = rentalOrderId;
	}
	
	/**
	 * 判断房间是否已经交租
	 * @return
	 */
	@Transient
	public boolean isPayed()
	{
		boolean payed = false;
		ValidAgreement validAgreement = getAgreement();
		if (validAgreement != null)
		{
			List<Fee> fees = validAgreement.getFees();
			if(fees != null && fees.size() > 1)
			{
				String today=DateTimeUtil.formatDate(new Date());
				Fee theFee = fees.get(0);
				if (theFee.getDateEndRecv().compareTo(today) > 0) 
				{
					payed = true;
				}
			}
		}
		
		return payed;
	}

	//0整租  1合租
	@Column(columnDefinition="tinyint(1) not null default 0")
	public Integer getRentType() {
		return rentType;
	}

	public void setRentType(Integer rentType) {
		this.rentType = rentType;
	}

	public Integer getRoomCount() {
		return roomCount;
	}

	public void setRoomCount(Integer roomCount) {
		this.roomCount = roomCount;
	}

	public Integer getHallCount() {
		return hallCount;
	}

	public void setHallCount(Integer hallCount) {
		this.hallCount = hallCount;
	}

	public Integer getToiletCount() {
		return toiletCount;
	}

	public void setToiletCount(Integer toiletCount) {
		this.toiletCount = toiletCount;
	}

	public Integer getDepositNum() {
		return depositNum;
	}

	public void setDepositNum(Integer depositNum) {
		this.depositNum = depositNum;
	}

	public Integer getPayNum() {
		return payNum;
	}

	public void setPayNum(Integer payNum) {
		this.payNum = payNum;
	}

	@ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
	public RoomConfig getRoomConfig() {
		return roomConfig;
	}

	public void setRoomConfig(RoomConfig roomConfig) {
		this.roomConfig = roomConfig;
	}

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	@JoinColumn(name = "room_id")
	@Filters({
		@Filter(name = "tenantUserNameFilter", condition = "agreement is not null and agreement.tenantUser.name like :tenantUserName"),
		@Filter(name = "rentStateFilter", condition = "rentState = :rentState"),
		@Filter(name = "rentEndDateFilter", condition = "agreement is not null and agreement.rentEndDate <= :rentEndDate")
	})
	public List<SubRoom> getSubRooms() {
		return subRooms;
	}

	public void setSubRooms(List<SubRoom> subRooms) {
		this.subRooms = subRooms;
	}

	@Transient
	public Boolean getNeedAddDeposit() {
		return needAddDeposit;
	}

	public void setNeedAddDeposit(Boolean needAddDeposit) {
		this.needAddDeposit = needAddDeposit;
	}

}
